/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package org.webos.java.explorer;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.webos.java.base.Application;
import org.webos.java.base.Frame;
import org.webos.java.base.FrameType;
import org.webos.java.base.RemoteMessage;
import org.webos.java.base.DBFileProvider;
import org.webos.java.base.fs.FileDescriptor;
import org.webos.java.base.fs.FileManager;
import org.webos.java.base.fs.FileProvider;
import org.webos.java.base.fs.FileType;
import org.webos.java.base.workspace.FrameHolder;
import org.webos.java.base.workspace.Workspace;
import org.webos.java.explorer.ftypes.ApplicationFileDescriptor;
import org.webos.java.explorer.ftypes.ApplicationFileType;
import org.webos.java.root.Log;
import org.webos.java.base.NetworkManager;

/**
 *
 * @author Kostya
 */

@FrameType(code="tree-panel", jsClass="explorer.TreePanel")
public class FileTreePanel extends Frame{

    private TreeItem root = null;
    private FileProvider rootProvider = null;
    private TreeItem selected = null;
    private FrameHolder mainFrameHolder = null;
    private Explorer explorer = null;
    private FileViewPanel viewPanel = null;
    private Workspace workspace = null;

    public FileTreePanel(Explorer explorer, Workspace ws, FrameHolder mainFrameHolder, FileViewPanel viewPanel) {
        super(explorer, ws);
        workspace = ws;
        setTitle("Browse files:");
        this.mainFrameHolder = mainFrameHolder;
        this.explorer = explorer;
        this.viewPanel = viewPanel;
        rootProvider = explorer.getSession().getRootProvider();
        FileDescriptor empty = new FileDescriptor();
        empty.setId("ROOT");
        empty.setName("ROOT");
        root = new TreeItem(empty, null);
        root.setCollapsed(false);
        TreeItem fileSystem = new TreeItem(rootProvider.getRootFile(), root);
        root.getChildren().add(fileSystem);
        TreeItem apps = new TreeItem(explorer.getSession().getApplicationProvider().getRootFile(), root);
        root.getChildren().add(apps);
//        Log.debug(this, "Root file is", root.getFile());
        expandTreeItem(fileSystem);
        expandTreeItem(apps);
    }

    private void expandTreeItem(TreeItem aItem){
        aItem.getChildren().clear();
        try {
            FileDescriptor[] descriptors = aItem.getFile().
                    getProvider().listFiles(aItem.getFile());
            for (FileDescriptor desc : descriptors) {
                aItem.getChildren().add(new TreeItem(desc, aItem));
            }
            aItem.setCollapsed(false);
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
    }

    private JSONObject fileDescriptorToJSON(TreeItem aItem){
        try {
            JSONObject object = new JSONObject();
            object.put("id", aItem.getFile().getId());
            object.put("name", aItem.getFile().getName());
            object.put("collapsed", aItem.isCollapsed());
            object.put("childrenAllowed", aItem.getFile().isChildrenAllowed());
            object.put("icon", aItem.getFile().getIcon());
            if(aItem.isCollapsed())
                return object;
            JSONArray arr = new JSONArray();
            for (TreeItem treeItem : aItem.getChildren()) {
                arr.put(fileDescriptorToJSON(treeItem));
            }
            object.put("children", arr);
            return object;
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
        return null;
    }

    @RemoteMessage
    public JSONObject expandElement(JSONObject aData){
        try {
            TreeItem item = findItemByID(aData.optString("id"), root);
            if(item!=null){
                expandTreeItem(item);
                return fileDescriptorToJSON(item);
            }
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
        return NetworkManager.getErrorObject(3, "Can't load files");
    }

    @RemoteMessage
    public JSONObject getTree(JSONObject aObject){
        try {
            JSONObject obj = new JSONObject();
            obj.put("tree", fileDescriptorToJSON(root));
            Log.debug(this, "Now tree is: "+fileDescriptorToJSON(root));
            if(selected!=null)
                obj.put("select", selected.getFile().getId());
            return obj;
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
        return null;
    }

    private TreeItem findItemByID(String id, TreeItem aItem){
        if(aItem!=null && aItem.getFile()!=null &&  aItem.getFile().getId()!=null && aItem.getFile().getId().equals(id))
            return aItem;
        if(!aItem.isCollapsed()){
            for (TreeItem treeItem : aItem.getChildren()) {
                TreeItem result = findItemByID(id, treeItem);
                if(result!=null)
                    return result;
            }
        }
        return null;
    }

    @RemoteMessage
    public JSONObject selectElement(JSONObject aData){
        try {
            //First, try to find selected element
            selected = findItemByID(aData.getString("id"), root);
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
        viewPanel.displayFile(selected==null?null:selected.getFile(), null);
        try {
            JSONObject result = new JSONObject();
            JSONArray arr = new JSONArray();
            if(selected==null)
                return result;
            for (FileType fType : selected.getFile().getAllowedFileTypes()) {
                if(fType.equals(FileManager.PLAIN_FILE_TYPE)){
                    for (String string : FileManager.getPlainFileTypes()) {
                        JSONObject obj = new JSONObject();
                        obj.put("code", string);
                        obj.put("name", FileManager.getFileType(string).getName());
                        arr.put(obj);
                    }

                }else{
                    JSONObject obj = new JSONObject();
                    obj.put("code", fType.getCode());
                    obj.put("name", fType.getName());
                    arr.put(obj);
                }
            }
            Log.debug(this, "Number of supported files: "+arr.length());
            result.put("file_types", arr);
            return result;
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
        return null;
    }

    @RemoteMessage
    public JSONObject openNewViewer(JSONObject aData){
        try {
            TreeItem item = findItemByID(aData.optString("id"), root);
            if(item==null)
                return NetworkManager.getErrorObject(7, "Can't find file");
            FileViewPanel newPanel = new FileViewPanel(explorer, workspace);
            newPanel.setCloseable(true);
            newPanel.setDynamic(true);
//            newPanel.setTreePanel(this);
            newPanel.setFrameHolder(mainFrameHolder);
            newPanel.setWorkspace(getParentWorkspace());
//            explorer.addFrame(newPanel, mainFrameHolder);
            newPanel.displayFile(item.getFile(), null);
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
        return null;
    }

    JSONObject removeFile(FileDescriptor aFile) {
        try {
            if(!aFile.isRemovable()){
                return NetworkManager.getErrorObject(6, "Can't remove unremovable file");
            }
            aFile.getProvider().removeFile(aFile, true);
            TreeItem item = findItemByID(aFile.getId(), root);
            if(item!=null){
                TreeItem parent = item.getParent();
                int index = parent.getChildren().indexOf(item);
                TreeItem newItem = parent;
                parent.getChildren().remove(item);
                if(index>=parent.getChildren().size())
                    index = parent.getChildren().size()-1;
                if(index>=0)
                    newItem = parent.getChildren().get(index);
                JSONObject obj = new JSONObject();
                obj.put("remove", item.getFile().getId());
                if(selected.getFile().getId().equals(item.getFile().getId()))
                    obj.put("select", newItem.getFile().getId());
                sendMessage("removeAndSelectElement", obj);
            }
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
        return null;
    }

    public JSONObject editFile(FileDescriptor aFile){
        try {
            FileType fileType = FileManager.getFileType(aFile.getType());
            if(fileType==null)
                return NetworkManager.getErrorObject(5, "Unknown file type");
            Object editor = fileType.getEditor(aFile, explorer);
            if(editor==null){
                return NetworkManager.getErrorObject(5, "Unknown file type");
            }
            showEditor(editor);
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
        return null;
    }

    private void showEditor(Object editor){
        if(editor instanceof Frame){
            Frame frame = (Frame) editor;
            frame.setCloseable(true);
            frame.setDynamic(true);
            frame.setFrameHolder(mainFrameHolder);
            frame.setWorkspace(getParentWorkspace());
//            explorer.addFrame(frame, mainFrameHolder);
        }
    }

    @RemoteMessage
    public JSONObject elementDblClick(JSONObject data){
        try {
            TreeItem item = findItemByID(data.getString("id"), root);
            if(item!=null && (item.getFile() instanceof ApplicationFileDescriptor)){
                ApplicationFileDescriptor desc = (ApplicationFileDescriptor) item.getFile();
                ApplicationFileType aft = (ApplicationFileType) FileManager.getFileType(item.getFile().getType());
                Application startApplication = aft.startApplication(desc, explorer.getSession());
            }
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
        return null;
    }

    @RemoteMessage
    public JSONObject newFile(JSONObject data){
        try {
            if(selected==null)
                throw new Exception("selected is null");
            FileType fileType = FileManager.getFileType(data.optString("type"));
            if(fileType==null)
                throw new Exception("fileType is null");
            //Also check this fileType in selected
            FileDescriptor newFile = new FileDescriptor();
            newFile.setParentFile(selected.getFile());
            newFile.setProvider(selected.getFile().getProvider());
            newFile.setType(data.optString("type"));
            Object editor = fileType.getEditor(newFile, explorer);
            if(editor==null)
                throw new Exception("editor is null");
            showEditor(editor);
            throw new Exception("editor is not frame");
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
//        Log.error(this, "That's strange...");
        return null;
    }

    public void fileUpdated(FileDescriptor aDescriptor, boolean aNewFile){
        if(!aNewFile){
            TreeItem item = findItemByID(aDescriptor.getId(), root);
            if(item!=null){
                try {
                    item.getFile().setName(aDescriptor.getName());
                    JSONObject obj = new JSONObject();
                    obj.put("element", fileDescriptorToJSON(item));
                    sendMessage("updateElementName", obj);
                } catch (Throwable t) {
                    Log.error(this, "Exception thrown: " + t.getMessage());
                    t.printStackTrace();
                }

            }
            return;
        }
        if(aDescriptor.getParentFile()!=null){
            TreeItem item = findItemByID(aDescriptor.getParentFile().getId(), root);
            if(item!=null){
                try {
                    JSONObject obj = new JSONObject();
                    expandTreeItem(item);
                    obj.put("element", fileDescriptorToJSON(item));
//                    obj.put("select", aDescriptor.getId());
                    sendMessage("loadAndSelectElement", obj);
                } catch (Throwable t) {
                    Log.error(this, "Exception thrown: " + t.getMessage());
                    t.printStackTrace();
                }
            }
            
        }
    }
}
